% *******************************************
% SECTION
% *******************************************
\section{任务4：第3关}

\ifdefined\arm
在之前的任务中，我们的目标服务器使用了 \texttt{memcpy()} 函数将数据复制到目标缓冲区。在这个任务中，我们将切换为 \texttt{strcpy()}，该函数在遇到零字节时停止复制。因此，我们的负载中不能再包含任何零。
\else
在之前的任务中，我们的目标服务器是32位程序。在这个任务中，我们将会切换到一个64位的服务器程序。
\fi
我们的新目标为 \texttt{10.9.0.7}，该地址运行的是 64 位版本的 \texttt{stack} 程序。
首先，我们将向这个服务器发送一条问候消息。下面将会打印出目标容器输出的消息。

\ifdefined\arm
% 对于 arm64
\begin{lstlisting}
// 在虚拟机（即攻击者机器）上
$ echo hello | nc 10.9.0.7 9090
Ctrl+C

server-3-10.9.0.7  | Got a connection from 10.9.0.1
server-3-10.9.0.7  | Starting stack
server-3-10.9.0.7  | Input size: 6
server-3-10.9.0.7  | Frame pointer (x29) inside foo():  0x0000fffffffff120
server-3-10.9.0.7  | Frame pointer (x29) inside bof():  0x0000ffffffffefc0
server-3-10.9.0.7  | Buffer's address inside bof():     0x0000ffffffffefe8
server-3-10.9.0.7  | ==== Returned Properly ====
\end{lstlisting}

你的任务是构建一个负载来利用服务器的缓冲区溢出漏洞。你最终的目标是在目标服务器上获得 root shell。你可以使用从任务1中的 shellcode。

\else
% 对于 amd64
\begin{lstlisting}
// 在虚拟机（即攻击者机器）上
$ echo hello | nc 10.9.0.7 9090
Ctrl+C

// 容器输出的消息
server-3-10.9.0.7 | Got a connection from 10.9.0.1
server-3-10.9.0.7 | Starting stack
server-3-10.9.0.7 | Input size: 6
server-3-10.9.0.7 | Frame Pointer (rbp) inside bof():  0x00007fffffffe1b0
server-3-10.9.0.7 | Buffer's address inside bof():     0x00007fffffffe070
server-3-10.9.0.7 | ==== Returned Properly ====
\end{lstlisting}

你可以看到帧指针和缓冲区地址的值现在为8字节长（而32位程序中是4字节）。
你的任务是构建一个负载来利用服务器的缓冲区溢出漏洞。你最终的目标是在目标服务器上获得 root shell。你可以使用从任务1中的 shellcode，但是你需要使用64位版本的 shellcode。

\fi

\paragraph{挑战。} 与32位机器上的缓冲区溢出攻击相比，在64位机器上的攻击更为困难。最困难的部分在于地址问题。虽然 x64 架构支持 64 位地址空间，但目前只允许从 \texttt{0x00} 到 \texttt{0x00007FFFFFFFFFFF} 的地址。这意味着每一个8字节地址的最高两位总是为零。这就带来了一个问题。

在我们的缓冲区溢出攻击中，我们需要将至少一个地址存储在负载中，并通过 \texttt{strcpy()} 函数将其复制到栈中。我们知道 \texttt{strcpy()} 函数会在遇到零时停止复制。因此，如果负载中间出现了一个零，则该零之后的内容将不会被复制到栈中。如何解决这个问题是这一关中最难的挑战之一。在你的报告中，你需要描述你是如何解决这个问题的。